package com.meshnetics.gb.stack.mac.primitives;

import com.meshnetics.gb.stack.core.event.StackEventType;
import com.meshnetics.gb.stack.core.event.StackIndication;
import com.meshnetics.gb.stack.core.layer.StackLayer;
import com.meshnetics.gb.stack.mac.RadioMACStackLayer;
import com.meshnetics.gb.stack.mac.type.Capability;
import com.meshnetics.gb.stack.mac.type.TypeCapability;
import com.meshnetics.gb.type.impl.ByteUtils;
import com.meshnetics.gb.type.impl.zcl.Bitmap;
import com.meshnetics.gb.type.impl.zcl.TypeBitmap;
import com.meshnetics.gb.type.impl.zcl.TypeUInt;
import com.meshnetics.gb.type.impl.zcl.UInt;

import java.nio.ByteBuffer;

/**
 * WRITE DESCRIPTION HERE
 *
 * @author AKovylin
 */
public class MACAssociateIndication extends StackIndication {
    public static final byte CODE = 0x47;

    private final UInt deviceAddress;
    private final Capability capabilityInfo;
    private final Bitmap secAndAcl;

    public MACAssociateIndication(long deviceAddress, Capability capabilityInfo, boolean securityUse, int aclEntry) {
        this(deviceAddress, capabilityInfo, securityUse, (byte) (aclEntry & 0xff));
    }

    public MACAssociateIndication(long deviceAddress, Capability capabilityInfo, boolean securityUse, byte aclEntry) {
        this(new Long(deviceAddress), capabilityInfo, Boolean.valueOf(securityUse), new Byte(aclEntry));
    }

    public MACAssociateIndication(Long deviceAddress, Capability capabilityInfo, Boolean securityUse, Byte aclEntry) {
        this((UInt) TypeUInt.UINT_64.fromObject(deviceAddress), capabilityInfo, securityUse, aclEntry);
    }

    public MACAssociateIndication(UInt deviceAddress, Capability capabilityInfo, Boolean securityUse, Byte aclEntry) {
        this(deviceAddress, capabilityInfo,
                Utils.createBitmapSecAndAcl(securityUse.booleanValue(), aclEntry.intValue()));
    }

    public MACAssociateIndication(UInt deviceAddress, Capability capabilityInfo, Boolean securityUse, Byte aclEntry,
                                  StackLayer source) {
        this(deviceAddress, capabilityInfo,
                Utils.createBitmapSecAndAcl(securityUse.booleanValue(), aclEntry.intValue()), source);
    }

    public MACAssociateIndication(ByteBuffer bb, RadioMACStackLayer layer) {
        super("macAssociateIndication", StackEventType.LME_TYPE, null, layer);
        ByteUtils.ensureNotNull(bb);
        ByteUtils.ensureCapacity(bb, 10);
        this.deviceAddress = (UInt) TypeUInt.UINT_64.deserialize(bb);
        this.capabilityInfo = (Capability) TypeCapability.TYPE.deserialize(bb);
        this.secAndAcl = (Bitmap) TypeBitmap.BITMAP_8.deserialize(bb);
        if (bb.remaining() > 0) {
            throw new IllegalArgumentException("unexpected content of ByteBuffer, " + bb.remaining() + " bytes left");
        }
    }

    public MACAssociateIndication(UInt deviceAddress, Capability capabilityInfo, Bitmap secAndAcl) {
        this(deviceAddress, capabilityInfo, secAndAcl, null);
    }

    public MACAssociateIndication(UInt deviceAddress, Capability capabilityInfo, Bitmap secAndAcl, StackLayer source) {
        super("macAssociateIndication", StackEventType.LME_TYPE, null, source);
        if (deviceAddress == null) {
            throw new NullPointerException("deviceAddress is null");
        }
        if (capabilityInfo == null) {
            throw new NullPointerException("capabilityInfo is null");
        }
        if (secAndAcl == null) {
            throw new NullPointerException("secAndAcl is null");
        }
        this.deviceAddress = deviceAddress.convert(TypeUInt.UINT_64);
        this.capabilityInfo = capabilityInfo;
        if (secAndAcl.getBits(5, 8) != 0) {
            throw new IllegalArgumentException("bits 5-7 of secAndAcl are reserved and should be all zeroes");
        }
        this.secAndAcl = secAndAcl;
    }

    public UInt getDeviceAddressType() {
        return deviceAddress;
    }

    public Long getDeviceAddress() {
        return (Long) deviceAddress.toObject();
    }

    public Capability getCapabilityInfo() {
        return capabilityInfo;
    }

    public Bitmap getSecAndAclType() {
        return secAndAcl;
    }

    public boolean isSecurityUse() {
        return secAndAcl.getBit(0);
    }

    public int getAclEntry() {
        return secAndAcl.getBits(1, 5);
    }

    public String toString() {
        return getName() + '(' + deviceAddress + ',' + capabilityInfo + ',' + secAndAcl + ')';
    }
}
